/* Copyright 2008. All rights reserved. */
package org.bling.process.render;

import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.bling.parser.BlingDocument;
import org.bling.parser.BlingParserException;
import org.bling.process.Request;
import org.bling.process.Response;
import org.bling.process.context.ApplicationContext;
import org.bling.swt.BlingLayout;
import org.bling.swt.BlingLayoutData;
import org.bling.util.Constants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

/**
 * <code>SingleDocumentInterfaceRenderer</code> - A Single Document Interface,
 * or SDI is a method of organizing graphical user interface applications into
 * individual windows that the operating system's window manager handles
 * separately.
 * 
 * This renderer creates an application which uses the concept of SDI
 * to create and maintain Shells.
 * 
 * @author DISBROB
 * 
 */
public class SingleDocumentInterfaceRenderer extends Renderer {

	private static final Logger LOG = LogManager
			.getLogger(SingleDocumentInterfaceRenderer.class);

	private static Map<String, Shell> shells = new HashMap<String, Shell>();
	private static Shell applicationParentShell = null;

	private Boolean isModal;
	
	public void render(Request request, Response response, BlingDocument doc) throws BlingParserException, BlingRenderException {

		Boolean isModal = request.isModal(); 
		String filename = response.getPath();
		Map<String, Object> context = response.getContext();
		
		this.isModal = isModal;
		
		Display display = (Display) ApplicationContext.getInstance()
				.getAttribute(Constants.ApplicationContextAttributes.DISPLAY);

		Shell shell = shells.get(filename);
		if (shell != null && !shell.isDisposed()) {
			/*
			 * The shell already exists, therefore we must
			 * carefully switch the controls from the existing
			 * shell.
			 */
			
			// First, destroy all controls on the current Shell
			Control[] controls = shell.getChildren();
			if (controls != null) {
				for (int i = 0; i < controls.length; i++) {
					controls[i].dispose();
				}
			}
			shell.pack();

			// Next, attach the new document to the existing shell
			doc.attach(shell, context);

			// Set focus to the shell, and ensure the controls are visible
			shell.setFocus();
			Point p = shell.getSize();

			// FIXME: UGH, Why must we resize for the controls to be visible
			shell.setSize(p.x + 1, p.y + 1);
			shell.setSize(p.x, p.y);
		} else {
			/*
			 * The Shell does not exist yet.  Create the new shell,
			 * and use the thread to poll for input from the user.
			 */
			shell = createShell(null);

			doc.attach(shell, context);
			shells.put(filename, shell);
			shell.open();

			while (!shell.isDisposed()) {
				if (!display.readAndDispatch()) {
					display.sleep();
				}
			}
			
			if (!isModal) {	shell.dispose(); }
		}

	}

	public void closeWindow(String filename) {
	   	Shell shell = shells.get(filename);
	   	if (shell != null) {
	   		shell.dispose();
	   	}
	}
	
	private Shell createShell(Shell parent) {

		Display display = (Display) ApplicationContext.getInstance()
		.getAttribute(
				Constants.ApplicationContextAttributes.DISPLAY);
		
		if (applicationParentShell == null) {
			applicationParentShell = new Shell(display);
		}
		
		if (parent == null) {
			Shell shell;
			if (isModal) {
				shell = new Shell(applicationParentShell, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
			} else {
				shell = new Shell(display);
			}

			return configureShell(shell);
		}

		Shell shell = new Shell(parent);
		return configureShell(shell);
	}

	private Shell configureShell(Shell shell) {
		shell.setLayout(new BlingLayout());
		BlingLayoutData data = new BlingLayoutData();
		data.marginTop = Constants.Margins.DEFAULT_COMPOSITE_TOP;
		data.marginBottom = Constants.Margins.DEFAULT_COMPOSITE_BOTTOM;
		data.marginLeft = Constants.Margins.DEFAULT_COMPOSITE_LEFT;
		data.marginRight = Constants.Margins.DEFAULT_COMPOSITE_RIGHT;
		shell.setLayoutData(data);
		return shell;
	}

}
